package com.kinyx.framework.business;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 区域工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KAreaUtils {

	/** 地球半径 */
	private static final double EARTH_RADIUS = 6378137;
	/** 经纬分片长 */
	private static double PIECE_LONG = Math.PI / 180.0d;
	/** 夹角计算倍数 */
	private static double MULTIPLE = 2.0d;

	/**
	 * 计算两点之间距离
	 * @param latitude 圆心纬度
	 * @param longitude 圆心经度
	 * @param latitudeX 待判定纬度
	 * @param longitudeX 待判定经度
	 * @return 距离
	 */
	public static double distance(@CanNotNull final double latitude, @CanNotNull final double longitude, //
			@CanNotNull final double latitudeX, @CanNotNull final double longitudeX) {
		final double pieceLatitude = latitude * PIECE_LONG;
		final double pieceLatitudeX = latitudeX * PIECE_LONG;
		final double distanceLatitude = pieceLatitude - pieceLatitudeX;
		final double distanceLongtitude = (longitude - longitudeX) * PIECE_LONG;
		return MULTIPLE * Math.asin(Math.sqrt(Math.pow(Math.sin(distanceLatitude / MULTIPLE), 2) + (Math.cos(pieceLatitude) * Math.cos(pieceLatitudeX) * Math.pow(Math.sin(distanceLongtitude / MULTIPLE), 2)))) * EARTH_RADIUS;
	}

	/**
	 * 校验是否在范围内（平面圆范围）
	 * @param latitude 圆心纬度
	 * @param longitude 圆心经度
	 * @param latitudeX 待判定纬度
	 * @param longitudeX 待判定经度
	 * @param radius 水平面半径
	 * @return true：在范围内；false：不在范围内；
	 */
	public static boolean check(@CanNotNull final double latitude, @CanNotNull final double longitude, //
			@CanNotNull final double latitudeX, @CanNotNull final double longitudeX, //
			@CanNotNull final int radius) {
		return distance(latitude, longitude, latitudeX, longitudeX) < radius;
	}

	/**
	 * 校验是否在范围内（柱状范围）
	 * @param latitude 圆心纬度
	 * @param longitude 圆心经度
	 * @param latitudeX 待判定纬度
	 * @param longitudeX 待判定经度
	 * @param radius 水平面半径（米）
	 * @param altitude 圆心高度
	 * @param altitudeX 待判定高度
	 * @param ogham 垂直面落差（米）
	 * @return true：在范围内；false：不在范围内；
	 */
	public static boolean check(@CanNotNull final double latitude, @CanNotNull final double longitude, //
			@CanNotNull final double latitudeX, @CanNotNull final double longitudeX, //
			@CanNotNull final int radius, //
			@CanNull final Double altitude, @CanNull final Double altitudeX, @CanNull final Integer ogham) {
		// 无高度做平面校验
		if ((altitude == null) || (ogham == null) || (altitudeX == null)) { return check(latitude, longitude, latitudeX, longitudeX, radius); }
		// 平面校验不通过直接返回
		if (!check(latitude, longitude, latitudeX, longitudeX, radius)) { return false; }
		// 平面校验通过，校验高度。
		return Math.abs(altitude - altitudeX) < ogham;
	}

}
